/**
 * VcDrawCore类表示一个单例对象，用于管理地图并提供添加多边形和标记的方法。
 */
class VcDrawCore {
  private static instance: VcDrawCore;

  private map: Cesium.Viewer | null = null; // 定义地图属性

  private drawingsRef: any | null = null; // 绘画结束监听

  private circularRingEntitys: any = [];

  private drawCenterPoint: any = null;
  private drawEndPoint: any = null;
  private isEditing = false;

  // 定义飞行参数
  private lon = 0;
  private lat = 0;
  private height = 0;
  private angle = 0;
  private speed = 0.05; // 旋转速度

  private constructor() {
    // 私有构造函数，防止实例化
  }

  /**
   * 返回VcDrawCore类的实例。
   * @returns VcDrawCore实例。
   */
  public static getInstance(): VcDrawCore {
    if (!VcDrawCore.instance) {
      VcDrawCore.instance = new VcDrawCore();
    }
    return VcDrawCore.instance;
  }

  /**
   * 设置VcDrawCore实例的地图。
   * @param map - Cesium 地图对象。
   */
  public setMap(map: Cesium.Viewer, drawingsRef: any) {
    this.map = map;
    this.drawingsRef = drawingsRef;
    this.addEventHandler();
  }

  /**
   * 画一个圆
   * @param center 中心经纬度
   * @param radius 半径距离【米】
   */
  public drawCircle(center: [number, number], radius: number) {
    const viewer = this.map as Cesium.Viewer;
    viewer.entities.add({
      position: Cesium.Cartesian3.fromDegrees(center[0], center[1]),
      ellipse: {
        semiMinorAxis: radius, // 指定半短轴的数字属性。
        semiMajorAxis: radius, // 指定半长轴的数值属性。
        rotation: Cesium.Math.toRadians(-40.0), // 一个数字属性，指定椭圆从北方逆时针旋转。
        material: Cesium.Color.RED.withAlpha(0.5), // 圆的颜色和透明度
        outline: true, // 一个布尔属性，指定是否勾勒出椭圆。
        outlineColor: Cesium.Color.WHITE, // 一个属性，指定轮廓的 颜色
        outlineWidth: 4, // 一个数字属性，指定轮廓的宽度。
        stRotation: Cesium.Math.toRadians(22), //  一个数字属性，指定椭圆纹理从北方逆时针旋转。
      },
    });
  }
  /**
   * 计算圆边坐标
   * @param {Array<number>} lnglat 经纬度坐标数组
   * @param {number} radius 半径
   */
  public computedCircle(lnglat, radius) {
    const r = 6371000.79;
    const phase = (2 * Math.PI) / 360;
    const points: any = [];
    for (let i = 0; i < 360; i += 5) {
      const dx = radius * Math.cos(i * phase);
      const dy = radius * Math.sin(i * phase);

      const lng = dx / ((r * Math.cos((lnglat[1] * Math.PI) / 180) * Math.PI) / 180);
      const lat = dy / ((r * Math.PI) / 180);
      const newLng = lnglat[0] + lng;
      points.push([newLng, lnglat[1] + lat]);
    }
    return points;
  }
  private getLonOrLat(cartesian) {
    const viewer = this.map as Cesium.Viewer;
    const cartesianCoordinate = viewer.scene.pickPosition(cartesian);
    if (!cartesianCoordinate) {
      return null;
    }
    const cartographic = Cesium.Cartographic.fromCartesian(cartesianCoordinate);
    const longitude = Cesium.Math.toDegrees(cartographic.longitude);
    const latitude = Cesium.Math.toDegrees(cartographic.latitude);
    const height = cartographic.height;
    const position = {
      longitude: Number(longitude.toFixed(6)),
      latitude: Number(latitude.toFixed(6)),
      altitude: Number(height.toFixed(2)),
    };
    return position;
  }
  private addEventHandler() {
    const viewer = this.map as Cesium.Viewer;
    const eventHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

    eventHandler.setInputAction((event) => {
      if (this.isEditing) {
        const position = this.getLonOrLat(event.endPosition);
        // console.log(position);

        if (position) {
          console.log('经度: ' + position.longitude + ', 纬度: ' + position.latitude);
          if (this.drawCenterPoint != null) {
            const v = this.getDistances(
              this.drawCenterPoint[0],
              this.drawCenterPoint[1],
              position.longitude,
              position.latitude
            );
            this.drawEndPoint = [position.longitude, position.latitude];
            this.drawCircleRing(this.drawCenterPoint, v);
          }
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  }

  // 根据经纬度计算距离，参数分别为第一点的经度 纬度，；第二点的经度 纬度，
  public getDistances(lng1, lat1, lng2, lat2) {
    const EARTH_RADIUS = 6371000.79; // 地球半径
    const radLat1 = (lat1 * Math.PI) / 180.0; //lat1 * Math.PI / 180.0=>弧度计算
    const radLat2 = (lat2 * Math.PI) / 180.0;
    const a = radLat1 - radLat2;
    const b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0;
    const s: number =
      2 *
      Math.asin(
        Math.sqrt(
          Math.pow(Math.sin(a / 2), 2) +
            Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
        )
      );
    const sm = s * EARTH_RADIUS;
    console.log('===========>', sm);
    return sm;
  }
  /**
   * 画一个圆环
   * @param center 中心经纬度
   * @param radius 半径距离【米】
   */
  public drawCircleRing(center: [number, number], radius: number) {
    this.clearCircleRing();
    if (radius <= 0) {
      return;
    }
    const viewer = this.map as Cesium.Viewer;
    this.drawCenterPoint = center;
    const circleCoordinates = this.computedCircle([center[0], center[1]], radius);
    const positions: any = [];
    circleCoordinates.forEach((coord) => {
      positions.push(Cesium.Cartesian3.fromDegrees(coord[0], coord[1]));
    });
    //闭环
    positions.push(Cesium.Cartesian3.fromDegrees(circleCoordinates[0][0], circleCoordinates[0][1]));

    const circularRingEntity = viewer.entities.add({
      name: 'c_01_c',
      polyline: {
        positions: positions, //Cesium.Cartesian3.fromDegreesArray(positions),
        width: 1,
        material: Cesium.Color.RED,
      },
    });
    this.circularRingEntitys.push(circularRingEntity);
    //中心点
    const line_positions: any = [];
    const line_end_point = {
      lon: 0,
      lat: 0,
    };
    line_positions.push(Cesium.Cartesian3.fromDegrees(center[0], center[1]));
    if (this.drawEndPoint == null) {
      line_end_point.lon = circleCoordinates[0][0];
      line_end_point.lat = circleCoordinates[0][1];
    } else {
      line_end_point.lon = this.drawEndPoint[0];
      line_end_point.lat = this.drawEndPoint[1];
    }
    line_positions.push(Cesium.Cartesian3.fromDegrees(line_end_point.lon, line_end_point.lat));
    const lineEntity = viewer.entities.add({
      name: 'c_02_line',
      polyline: {
        positions: line_positions, //Cesium.Cartesian3.fromDegreesArray(positions),
        width: 2,
        material: Cesium.Color.RED,
      },
      point: {
        pixelSize: 4,
        color: Cesium.Color.RED,
      },
      position: Cesium.Cartesian3.fromDegrees(center[0], center[1]),
    });
    this.circularRingEntitys.push(lineEntity);

    const labelpPositiont = [
      (center[0] + line_end_point.lon) / 2.0,
      (center[1] + line_end_point.lat) / 2.0,
    ];
    const lableEntity = viewer.entities.add({
      name: 'c_03_label',
      id: 'c_03_label',
      label: {
        text: (radius / 1000.0).toFixed(2) + 'km',
        font: '24px Helvetica',
      },
      position: Cesium.Cartesian3.fromDegrees(labelpPositiont[0], labelpPositiont[1]),
    });
    this.circularRingEntitys.push(lableEntity);
  }
  public clearCircleRing() {
    if (this.circularRingEntitys != null) {
      const viewer = this.map as Cesium.Viewer;
      this.circularRingEntitys.forEach((node) => {
        viewer.entities.remove(node);
      });
      this.circularRingEntitys.splice(0);
    }
  }
  /**
   * 画一个矩形
   * @param p1
   * @param p2
   */
  public drawRectangle(p1: [number, number], p2: [number, number]) {
    const viewer = this.map as Cesium.Viewer;
    viewer.entities.add({
      rectangle: {
        coordinates: Cesium.Rectangle.fromDegrees(p1[0], p1[1], p2[0], p2[1]), // 最西、最南、最东、最北
        material: new Cesium.ColorMaterialProperty(Cesium.Color.RED.withAlpha(0.5)), // 圆的颜色和透明度
        outline: true, // 一个布尔属性，指定是否勾勒出椭圆。
        outlineColor: Cesium.Color.WHITE, // 一个属性，指定轮廓的 颜色
        outlineWidth: 4, // 一个数字属性，指定轮廓的宽度。
        stRotation: Cesium.Math.toRadians(22), //  一个数字属性，指定椭圆纹理从北方逆时针旋转。
      },
    });
  }

  /**
   * 画多边形
   * @param positions
   */
  public drawPolygon(positions: [[number, number]]) {
    const viewer = this.map as Cesium.Viewer;
    const degreesArray: [number] = [] as any;
    positions.forEach((node) => {
      degreesArray.push(node[0]);
      degreesArray.push(node[1]);
    });
    viewer.entities.add({
      polygon: {
        hierarchy: new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(degreesArray)),
        material: new Cesium.ColorMaterialProperty(Cesium.Color.RED.withAlpha(0.5)), // 圆的颜色和透明度
        outline: true, // 一个布尔属性，指定是否勾勒出椭圆。
        outlineColor: Cesium.Color.WHITE, // 一个属性，指定轮廓的 颜色
        outlineWidth: 4, // 一个数字属性，指定轮廓的宽度。
        stRotation: Cesium.Math.toRadians(22), //  一个数字属性，指定椭圆纹理从北方逆时针旋转。
      },
    });
  }

  public calculatePointAtAngleFromSouth(lat, lng, radius, angle) {
    // 将中心点的经纬度和角度转换为弧度
    const latRad = (lat * Math.PI) / 180;
    const lngRad = (lng * Math.PI) / 180;
    const r = (radius * Math.PI) / 180;
    // 将角度从南开始的基准转换为以北为基准的标准角度
    const adjustedAngle = (angle + 180) % 360; // 将从南的角度转为从北的角度
    const angleRad = (adjustedAngle * Math.PI) / 180; // 转换为弧度

    // 使用球面几何公式计算新点的纬度和经度
    const newLatRad = Math.asin(
      Math.sin(latRad) * Math.cos(r) + Math.cos(latRad) * Math.sin(r) * Math.cos(angleRad)
    );

    const newLngRad =
      lngRad +
      Math.atan2(
        Math.sin(angleRad) * Math.sin(r) * Math.cos(latRad),
        Math.cos(r) - Math.sin(latRad) * Math.sin(newLatRad)
      );

    // 将弧度转换回度数
    const newLat = (newLatRad * 180) / Math.PI;
    const newLng = (newLngRad * 180) / Math.PI;

    return { lat: newLat, lon: newLng };
  }
  public flyTo(lon: number, lat: number, height: number, duration = 1.0) {
    const viewer = this.map as Cesium.Viewer;
    if (viewer) {
      viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
        orientation: {
          heading: Cesium.Math.toRadians(0), // east, default value is 0.0 (north) //东西南北朝向
          pitch: Cesium.Math.toRadians(-90), // default value (looking down)  //俯视仰视视觉
          roll: 0.0, // default value
        },
        duration: duration, //1秒到达战场
      });
    }
  }
  // 环绕视角
  public aroundView(lon: number, lat: number, height: number, duration = 1.5) {
    const viewer = this.map as Cesium.Viewer;
    if (viewer) {
      const fv = ((height * Math.tan(60 * (Math.PI / 180.0))) / 111320.0) * 0.9;
      viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(lon, lat - fv, height),
        orientation: {
          heading: Cesium.Math.toRadians(0), // east, default value is 0.0 (north) //东西南北朝向
          pitch: Cesium.Math.toRadians(-35), // default value (looking down)  //俯视仰视视觉
          roll: Cesium.Math.toRadians(0), // default value
        },
        duration: duration, //1秒到达战场
      });
    }
  }
  public setView(lon: number, lat: number, height: number) {
    const viewer = this.map as Cesium.Viewer;
    if (viewer) {
      const fv = ((height * Math.tan(60 * (Math.PI / 180.0))) / 111320.0) * 0.9;
      viewer.camera.setView({
        destination: Cesium.Cartesian3.fromDegrees(lon, lat - fv, height),
        orientation: {
          heading: Cesium.Math.toRadians(0), // east, default value is 0.0 (north) //东西南北朝向
          pitch: Cesium.Math.toRadians(-35), // default value (looking down)  //俯视仰视视觉
          roll: Cesium.Math.toRadians(0), // default value
        },
      });
    }
  }
  /**
   * 开始环绕动画
   * timeOut 动画结束时间【默认10s】
   */
  public around(lon: number, lat: number, height: number, timeOut = 10) {
    const viewer = this.map as Cesium.Viewer;
    this.lon = lon;
    this.lat = lat;
    this.height = height;

    viewer.clock.onTick.addEventListener(this.clockRun);
    setTimeout(() => {
      viewer.clock.onTick.removeEventListener(this.clockRun);
      viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
    }, timeOut * 1000);
  }
  /**
   * 关闭环绕动画
   */
  public closeAround() {
    const that = VcDrawCore.instance;
    const viewer = that.map as Cesium.Viewer;
    viewer.clock.onTick.removeEventListener(that.clockRun);
    viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
  }
  private clockRun() {
    const that = VcDrawCore.instance;
    const viewer = that.map as Cesium.Viewer;
    // 更新角度
    that.angle += that.speed; // 朝向

    const offset = new Cesium.HeadingPitchRange(
      Cesium.Math.toRadians(that.angle),
      Cesium.Math.toRadians(-35),
      that.height
    );

    const fv = ((that.height * Math.tan(60 * (Math.PI / 180.0))) / 111320.0) * 0.9;
    // 计算相机的新位置，保持在圆环路径上
    const pv = that.calculatePointAtAngleFromSouth(that.lat, that.lon, fv, that.angle);

    const center2 = Cesium.Cartesian3.fromDegrees(pv.lon, pv.lat, that.height);

    // 使用 lookAt 方法，让相机绕中心点飞行并始终朝向中心点
    viewer.camera.lookAt(center2, offset);
  }

  /**
   * 打开画图工具
   * @param name 'pin' | 'point' | 'polyline' | 'polygon' | 'rectangle' | 'regular' | 'circle'
   */
  public toggleAction(name: string) {
    console.log('toggleAction', name);
    try {
      this.drawingsRef.toggleAction(name);
    } catch (e) {
      console.log('toggleAction error', e);
    }
  }

  /**
   * 打开画marker工具
   */
  public openMarkerTool() {
    if (this.map == null) {
      return;
    }
    this.toggleAction('pin');
  }

  /**
   * 打开画原点工具
   */
  public openPointTool() {
    if (this.map == null) {
      return;
    }
    this.toggleAction('point');
  }

  /**
   * 打开画线工具
   */
  public openLineTool() {
    if (this.map == null) {
      return;
    }
    this.toggleAction('polyline');
  }

  /**
   * 打开画多边形工具
   */
  public openPolygonTool() {
    if (this.map == null) {
      return;
    }
    this.toggleAction('polygon');
  }

  /**
   * 打开画矩形工具
   */
  public openRectangleTool() {
    if (this.map == null) {
      return;
    }
    this.toggleAction('rectangle');
  }

  /**
   * regular 六边形
   */
  public openRegularTool() {
    if (this.map == null) {
      return;
    }
    this.toggleAction('regular');
  }

  /**
   * 打开画圆工具
   */
  public openCircleTool() {
    if (this.map == null) {
      return;
    }
    this.toggleAction('circle');
  }

  /**
   * 关闭画图工具
   */
  public closeDrawTool() {
    if (this.map == null) {
      return;
    }
  }
  /**
   * 清理地图上的所有标记和多边形。
   */
  public clearMapDraw() {
    if (this.map == null) {
      return;
    }
    this.drawingsRef.clearAll();
  }
}

export const VcDraw = {
  core: VcDrawCore.getInstance(),
};
